25 国际化特性

地区:可能是国家、洲或省活着一种特定的文化。
扩展:1994年通过的ISO C标准修正草案1提供了编写额外国际化程序增加的额外库

  • iso646.h
  • wctype.h
  • wchar.h

25.1 locale.h:本地化

标准库中依赖地区的部分:

  • 数值的格式:例如,一些地区小数点是一个圆点(297.48),而在另一些地方则是逗号(297,48)
    货币的格式:例如,不同国家的货币符号不同
    字符集:例如,亚洲国家通常比西方国家需要更大的字符集
    日期和时间的表示形式:例如,一些地方习惯在写日期时先写月(8/24/97),而另一些地方习惯先写日(24/8/97)

25.1.1 类别

说明:locale.h提供了一系列的以LC_开头的宏来,这些宏分别对某些库的某些方面的行为产生影响。
扩展:C语言的实现提供了其它类型并且定义了上面未列出的以LC_开头的宏。

影响 头文件 相关章节 备注
LC_COLLATE strcoll函数 string.h 23.5 字符串比较函数
strxfrm函数 string.h 23.5 字符串比较函数
LC_CTYPES isdigit函数 ctype.h 23.4
isxdigit函数 ctype.h 23.4
多字节函数 stdlib.h 25.2.1
LC_MONETARY localeconv函数 locale.h 影响该函数返回的货币格式信息
LC_NUMERIC 格式化输入输出函数中使用的小数点字符 stdio.h 22 比如printfscanf
字符串转换函数 stdlib.h 26.2.1 atofstrtod
localeconv函数 locale.h 该函数返回的非货币格式信息
LC_TIME strftime函数 time.h 26.3.2 该函数用于将时间转换为字符串

25.1.2 setlocale函数


描述:用来修改当前的地区,也可以用来获取当前地区的信息。
参数:
1: 可以针对一种类型,也可以针对所有类型

参数1取值 说明
LC_COLLATE、LC_CTYPE、LC_MONETARY 、LC_NUMERIC、LC_TIME 针对一种类型
LC_ALL 针对所有类型

2: C语言标准为第二个参数仅定义了两种可能值:”c”或” “。其他地区可以针对不同的实现定义。

参数二取值 说明 备注
"C" 按正常方式执行,小数点是一个句点
" " 换到本地模式(native locale) C语言标准没有定义切换到本地模式的具体影响
"Germany" 德国 部分编译器支持
类似en_GB.WIN1252格式的字符串 英语_英国.Windows多语言字符集 一些常用编译器支持
NULL 不设置任何东西 仅仅返回指向与当前地区类型的设置相关联的字符串

技巧:如果需要获得与当前地区关联字符串,可以在调用setlocale函数时第一个参数给LC_ALL,第二个参数给NULL.
原型:locale.h

1
2
3
4
5
6
/**
* @param {int} category 指定影响哪些库的哪些方面
* @param {char *} locale 指定地区
* @return {char *} 指向字符串(可能是地点名字自身):成功;空指针:调用失败
*/

char *setlocale(int category, const char *locale);
1
2
// 任意程序执行开始时,都会隐含执行调用
setlocale(LC_ALL, "C");
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/* 获取地区信息字符串 */
char *temp, *old_locale;

/* 获取当前地区信息 */
temp = setlocale(LC_ALL, NULL);

/* 检查是否获取到信息 */
if (temp == NULL) {

}
/* 为信息的副本分配空间 */
old_locale = malloc(strlen(temp) + 1);
if (old_locale == NULL) {
/* 分配内存失败 */
}
/* 将信息拷贝进去 */
strcpy(old_locale, temp);

/* 切换到本地模式 */
setlocale(LC_ALL, "");

/* 按照存储的信息恢复就的地区设置 */
setlocale(LU_ALL, old_locale);

25.1.3 localeconv函数


描述:获取当前地区的各种信息
原型:locale.h

1
2
3
4
/**
* @return {struct lconv *} 指向包含当前地区的详细信息的结构体指针
*/

struct lconv *localeconv(void);

struct lconv


说明:存储当前地区的各种信息的结构体
结构成员:char *char两种类型

char *型成员
Alt text
char型成员
Alt text

p_sign_posn和n_sign_posn的值:
Alt text

案例:用于美国和意大利两国的lconv结构成员的货币型常用值
Alt text

Alt text

25.2 多字节字符和宽字符

字符集(美国):

分类 地位
ASCII 主流计算机采用
EBCDIC 其他计算机

可扩展字符集:C语言允许编译器提供一种可扩展的字符集

可扩展字符集编码 说明
多字节字符(multibyte character) 一个或多个字节表示一个可扩展的字符
宽字符(wide character) 一种其值表示字符的整数,具有相同的字节数

基本字符:任何可扩展的字符集必须包含C语言要求的基本字符(即字母数字运算符标点符空白字符),而且这些字符要求是单字节的。
用途:c语言同时提供了多字节字符和宽字符用于不同的目的

  • 多字节字符:多用于输入/输出,因为输入/输出设备经常是面向字节的
  • 宽字符:更适用于程序内部,因为没个宽字符占有相同的空间,便于程序内部操作。

技巧:程序可以读入多字节字符,然后转为便于程序内部操作的宽字符格式,谈后再把宽字符转换回用于输出的多字节格式。

25.2.1 多字节字符

空字符(\0):无论移位状态如何,c标准都要求\0始终用来表示空字符。而且,\0不能是多字节字符的第二个(或之后)字节。
按是否依赖状态分类:mblen函数mbtowc函数wctomb函数都可用检测对字节字符是否是依赖状态的。只要其char *型参数给予NULL实参,则返回非零说明依赖状态;返回零说明依赖状态。

分类 说明 例子
依赖状态编码(state-dependent enciding) 每个多字节字符序列都以初始移位状态(initial shift state)开始,序列中稍后遇到的一些多字节字符会改变移位状态,并且会影响后续字节的含义 日本的JIS编码
不依赖状态编码 每个字符要求一个或者两个字节,但是双字节字符的第一个字节可以始终区别与单字节字符 日本的Shift-JIS编码

相关宏:两个

头文件 含义 备注
MB_LEN_MAX limit.h 任意支持区域的最大值
MB_CUR_MAX stdlib.h 当前区域的最大值 改变地区可能会影响多字节字符的解释

25.2.2 宽字符

说明:采用特殊实现(比如unsigned short int)支持的所有宽字符都要求相同的字节数。
宽字符类型:wchar_t (stddef.hstdlib.h)
宽字符常量和宽字符串常量:L字符(串)常量

  • 宽字符常量:L'a'
  • 宽字符串常量:L"abc"

Unicode

说明:非常重要的固定长度编码的字符集。
字符宽度:两个字节

25.2.3 多字节字符/宽字符转换函数

mblen函数


说明:检测s是否指向形成由效多字节字符的字节序列
原型:stdlib.h

1
2
3
4
5
6
/**
* @param {char *} s 多字节字符的字节序列
* @param {size_t} a 最多能将测的字节的数量(通常为MB_CUR_MAX)
* @return {int} 字符中的字节数:是多字节字符序列;0:空字符;-1:不是多字节字符序列
*/

int mblen(const char *s, size_t a);

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/**
* 确定字符串是否由有效的多字节字符构成
* @param {char *s} 指向以空字符结尾的普通字符串
* @return {int} 0或-1
*/

int mbcheck (const char *s) {
int n;
// 通过mblen(NULL, 0)设置该函数的移位状态,以便可以正确解释字符串中稍后的字符
for (mblen(NULL, 0); ; s += n) {
// 当
if ((n = mblen(s, MB_CUR_MAX)) <= 0) {
return n;
}
}
}

mbtowc函数


说明:将多字节字符串转换为宽字符
原型:stdlib.h.h

1
2
3
4
5
6
7
/**
* @param {wchar_t *} pwc 函数将存储结果的变量
* @para {char *} s 多字节字符
* @param {size_t} n 最大将检测的字节的数量
* @return {int} 字符中的字节数:是多字节字符;0:空字符;-1:不是多字节字符
*/

int mbtowc(wchar_t *pwc, const char *s, size_t n);

wctomb函数


说明:把宽字符转换为多字节字符
原型:stdlib.h

1
2
3
4
5
6
/**
* @param {char *} s 存储结果的变量(不回附加空字符)
* @param {wchar_t} wchar 多字节字符
* @return {int} 字符中的字节数:有效;1:空字符;-1:无效
*/

int wctomb(char *s, wchar_t wchar);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/**
* 将测是否可以把宽字符的字符串转换为有效的多字节字符
* @param {wchar_t *} wcs 宽字符序列
* @return {int} -1:不可以;0:可以
*/

int wccheck (wchar_t *wcs) {
char buf[MB_len_max];
int n;
for (wctpmb (NULL, 0); ; ++wcs) {
if ((n = wctomb(buf, *wcs)) <= 0) {
return -1;
}
else if (buf[n - 1] == '\0') {
return 0;
}
}
}

25.2.4 多字节字符/宽字符串函数

mbstowcs函数


说明:把多字节字符序列转换为宽字符序列

  • 如何进行转换依赖于当前地区的LC_CTYPES类别
  • 当达到上限或者遇到(存储在宽字符数组中的)\0时,函数停止
  • 假设要转换的字符串以初始迁移状态开始

原型:stdlib.h

1
2
3
4
5
6
7
/**
* @param {whcar_t *} pwcs 宽字符数组
* @param {char *} s 待转换的多字节字符数组
* @param {size_t} 可以存储在数组中的宽字符数量
* @return {size_t} 修改的数组元素的数量(不包括末尾的`\0`):一切顺利;-1:遇到无效的多字节字符
*/

size_t mbstowcs(wchar_t * restrict pwcs, const char * restrict s, size_t n);

wcstombs函数

说明:把宽字符序列转换为多字节字符序列

  • 如何进行转换依赖于当前地区的LC_CTYPES类别
  • 当达到上限或者遇到(自己存入的)\0时,函数停止
  • 产生的字符串是以初始迁移状态开始的

原型:stdlib.h

1
2
3
4
5
6
7
/**
* @param {char *} s 多字节字符数组
* @param {whcar_t *} pwcs 待转换的宽字符数组
* @param {size_t} 可以存储在数组中的多字节字符数量
* @return {size_t} 修改的数组元素的数量(不包括末尾的`\0`):一切顺利;-1:遇到无法转换为对应多字节字符的宽字符
*/

size_t wcstombs(const char * restrict s, wchar_t * restrict pwcs, size_t n);

25.3 三字符序列

三字符序列(trigraph sequence):简称“三字符”,是一种三个字符的字符码。以??字符的形式出现。
用途:可以替换ASCII中的一些特殊字符。
兼容性:尽管不是一直需要,但是所有标准C编译器都要求接受三字符序列。
技巧:字符串中的??可能会被编译器作为三字符序列的开始标志,可以通过使用\将第二个?转义(即?\?)来避免。
Alt text

1
2
3
4
5
6
#include <stdio.h>

int main () {
printf("Hello, world\n");
return 0;
}
1
2
3
4
5
??=include <stdio.h>
int main () ??<
printf("Hello, world??/n");
return 0;
??>